﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Xml;
using System.IO;
using System.Text;

namespace DBLOG.KmlWrapper
{
    public class KmlGenerator
    {
        #region Properties

        public List<KmlPlacemark> KmlPlacemarks { get; set; }

        public string KmlName { get; set; }
        public string KmlDescription { get; set; }

        private XmlTextWriter _xmlWriter;
        private StringWriter _stringWriter;

        #endregion

        /// <summary>
        /// Create a new KmlGenerator without adding any placemarks
        /// </summary>
        public KmlGenerator()
        {
            List<KmlPlacemark> kmlPlacemarks = new List<KmlPlacemark>();
            KmlPlacemarks = kmlPlacemarks;
            setup();
        }

        /// <summary>
        /// Create a new KmlGenerator with a collection of placemarks
        /// </summary>
        /// <param name="kmlPlacemarks">Collection of KML placemarks</param>
        public KmlGenerator(List<KmlPlacemark> kmlPlacemarks)
        {
            KmlPlacemarks = kmlPlacemarks;
            setup();
        }

        /// <summary>
        /// Create a new KmlGenerator with a KML document
        /// </summary>
        /// <param name="kmlDocument">KML document</param>
        public KmlGenerator(KmlDocument kmlDocument)
        {
            KmlPlacemarks = kmlDocument;
            setup();
        }

        /// <summary>
        /// This method is called from the KmlGenerator constructor to initialize values
        /// </summary>
        private void setup()
        {
            _stringWriter = new StringWriter();
            _xmlWriter = new XmlTextWriter(_stringWriter);

            _xmlWriter.WriteStartDocument();
            _xmlWriter.WriteStartElement("kml", "http://www.opengis.net/kml/2.2");

            //_xmlWriter.WriteElementString("name", "dblog.bergdaniel.se KML");
            //_xmlWriter.WriteElementString("description", "KML generated with code from http://dblog.bergdaniel.se");
        }

        /// <summary>
        /// Add KML placemark to collection
        /// </summary>
        /// <param name="name">The name of the placemark</param>
        /// <param name="description">KML placemark description</param>
        /// <param name="kmlPoint">The KML point that the placemark should contain</param>
        public void AddKmlPlacemark(string name, string description, KmlPoint kmlPoint)
        {
            if (name != null && description != null && kmlPoint != null)
                KmlPlacemarks.Add(new KmlPlacemark() { Description = description, KmlPoint = kmlPoint, Name = name });
            else if (name != null && description != null)
                KmlPlacemarks.Add(new KmlPlacemark() { Description = description, Name = name });
            else
                throw new KmlWrapperException("Name, description or KmlPoint cannot be null when adding a placemark!");
        }

        /// <summary>
        /// Add KML placemark to collection
        /// </summary>
        /// <param name="name">The name of the placemark</param>
        /// <param name="description">KML placemark description</param>
        public void AddKmlPlacemark(string name, string description)
        {
            if (name != null && description != null)
                KmlPlacemarks.Add(new KmlPlacemark() { Description = description, Name = name });
            else
                throw new KmlWrapperException("Name or description cannot be null when adding a placemark!");
        }

        /// <summary>
        /// Add a KML placemark with name, description, point and style
        /// </summary>
        /// <param name="name">Name</param>
        /// <param name="description">Description</param>
        /// <param name="kmlPoint">KmlPoint</param>
        /// <param name="kmlStyle">KmlStyle</param>
        public void AddKmlPlacemark(string name, string description, KmlPoint kmlPoint, KmlStyle kmlStyle)
        {
            if (name != null && description != null && kmlPoint != null)
                KmlPlacemarks.Add(new KmlPlacemark() { Description = description, KmlPoint = kmlPoint, Name = name, KmlStyle = kmlStyle });
            else
                throw new KmlWrapperException("Name, description or KmlPoint cannot be null when adding a placemark!");
        }

        /// <summary>
        /// Method must be called to generate KML
        /// </summary>
        public void GenerateKml()
        {
            _xmlWriter.WriteStartElement("Document");

            List<string> styleNames = new List<string>();
            foreach (KmlPlacemark p in this.KmlPlacemarks)
            {
                if(p.KmlStyle!=null && !styleNames.Contains(p.KmlStyle.Id))
                {
                    _xmlWriter.WriteStartElement("Style");
                    _xmlWriter.WriteAttributeString("id", p.KmlStyle.Id);
                        _xmlWriter.WriteStartElement("IconStyle");
                            _xmlWriter.WriteStartElement("Icon");
                                _xmlWriter.WriteElementString("href", p.KmlStyle.IconImageUrl);
                            _xmlWriter.WriteEndElement();
                        _xmlWriter.WriteEndElement();
                    _xmlWriter.WriteEndElement();

                    styleNames.Add(p.KmlStyle.Id);
                }
            }

            foreach (KmlPlacemark p in this.KmlPlacemarks)
            {
                _xmlWriter.WriteStartElement("Placemark");
                _xmlWriter.WriteElementString("name", p.Name);
                _xmlWriter.WriteStartElement("description");
                    _xmlWriter.WriteCData(p.Description);
                _xmlWriter.WriteEndElement();

                if (p.KmlStyle != null)
                {
                    _xmlWriter.WriteStartElement("styleUrl");
                        _xmlWriter.WriteCData(p.KmlStyle.Id);
                    _xmlWriter.WriteEndElement();
                    _xmlWriter.WriteElementString("styleUrl", "#" + p.KmlStyle.Id);
                }
                if (p.KmlPoint != null)
                {
                    _xmlWriter.WriteStartElement("Point");
                    _xmlWriter.WriteElementString("coordinates",
                        string.Format("{0},{1}", p.KmlPoint.Longitude, p.KmlPoint.Latitude)
                        );
                    _xmlWriter.WriteEndElement();
                }
                _xmlWriter.WriteEndElement();
            }
            //Document element
            _xmlWriter.WriteEndElement();

            // Close root element
            _xmlWriter.WriteEndElement();

            // Close document
            _xmlWriter.WriteEndDocument();

            _xmlWriter.Close();
            _stringWriter.Close();
        }

        /// <summary>
        /// Overriden - return the KML document as a string
        /// </summary>
        /// <returns>KML string</returns>
        public override string ToString()
        {
            if (_stringWriter != null)
                return _stringWriter.ToString();
            else
            {
                GenerateKml();
                return _stringWriter.ToString();
            }
        }
    }
}
